package com.yanyeori.framework.redis.aspect;

import com.yanyeori.framework.core.constant.BaseWebCodeEnum;
import com.yanyeori.framework.core.exception.BusinessException;
import com.yanyeori.framework.core.util.StringUtil;
import com.yanyeori.framework.redis.annotation.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.lang.reflect.Method;

/**
 * redis分布式锁的切面
 *
 * @author chenkuan 2020/8/18
 */
@Slf4j
@Aspect
public class RedisLockAspect {

    private final RedissonClient redissonClient;

    public RedisLockAspect(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Pointcut("@annotation(com.yanyeori.framework.redis.annotation.RedisLock)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        RedisLock redisLock = method.getAnnotation(RedisLock.class);
        String lockKey = redisLock.lockKey();
        if (StringUtil.isEmpty(lockKey)) {
            lockKey = method.getDeclaringClass().getName() + "_" + method.getName();
        }
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(redisLock.waitTime(), redisLock.timeout(), redisLock.unit())) {
                return point.proceed();
            } else {
                log.warn("Thread [{}] did not acquire the lock, current lock details are: {}", Thread.currentThread().getName(), lock);
                throw new BusinessException(BaseWebCodeEnum.SERVER_BUSY.code(), BaseWebCodeEnum.SERVER_BUSY.msg());
            }
        } finally {
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
    }
}
